﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using cogency.representation;

namespace cogency.languages.csharp
{
    internal class CookComputingXmlRpcWriter : CSharpWriter
    {
        public CookComputingXmlRpcWriter(CodeWriterContext objectSrc)
            : base(objectSrc)
        {
        }

        public void CreateXmlRpcClientInterfaceDeclaration(RDeclaration decl)
        {
            string filename = string.Format("I{0}XmlRpcClientDeclaration.cs", decl.Name);
            string pathname = Path.Combine(RootDirectory, Namespace.Name, filename);
            Trace.TraceInformation("Creating {0}", pathname);

            using (Output = File.CreateText(pathname))
            {
                WriteCommonHeader();
                Output.Write("using CookComputing.XmlRpc;\r\n\r\n");
                BeginNamespace();

                Output.Write(Indent);
                Output.Write("public interface I");
                Output.Write(decl.Name);
                Output.Write("XmlRpcClientDeclaration : IXmlRpcProxy\r\n");
                BeginScope();
                foreach (RDeclaration member in decl.Items)
                {
                    Trace.Assert(member.DeclarationType == RDeclarationType.MethodDeclaration);
                    CreateXmlRpcClientInterfaceMethodDeclaration(member);
                }
                EndScope();
                EndNamespace();
            }
        }

        protected static string GetXmlRpcParameterType(RDeclaration decl)
        {
            switch (decl.DeclarationType)
            {
                case RDeclarationType.BuiltinTypeReference:
                    return GetCSharpType(decl.BuiltinType);

                case RDeclarationType.EnumReference:
                case RDeclarationType.ClassReference:
                    return "string";
            }
            throw new InvalidOperationException(string.Format("Unable to map {0} to a parameter type", decl));
        }

        protected void CreateXmlRpcClientInterfaceMethodDeclaration(RDeclaration decl)
        {
            WriteComment(decl);
            Output.Write(Indent);
            Output.Write("[XmlRpcMethod(\"");
            Output.Write(Namespace.Name);
            Output.Write(".");
            Output.Write(decl.Name);
            Output.Write("\")]\r\n");
            Output.Write(Indent);
            if (decl.ReturnType == null)
            {
                Output.Write("void ");
            }
            else
            {
                Output.Write(GetXmlRpcParameterType(decl.ReturnType));
                Output.Write(" ");
            }
            Output.Write(decl.Name);
            if (decl.HasItems)
            {
                Output.Write("(");
                int index = 1;
                int count = decl.Items.Count;
                foreach (RDeclaration member in decl.Items)
                {
                    Output.Write(GetXmlRpcParameterType(member));
                    Output.Write(" ");
                    Output.Write(member.Name);
                    if (index++ < count)
                    {
                        Output.Write(", ");
                    }
                }
                Output.Write(");\r\n\r\n");
            }
            else
            {
                Output.Write("();\r\n\r\n");
            }
        }

        public void CreateXmlRpcClientImplementation(RDeclaration decl)
        {
            string filename = string.Format("{0}XmlRpcClientImplementation.cs", decl.Name);
            string pathname = Path.Combine(RootDirectory, Namespace.Name, filename);
            Trace.TraceInformation("Creating {0}", pathname);

            using (Output = File.CreateText(pathname))
            {
                WriteCommonHeader();
                Output.Write("using CookComputing.XmlRpc;\r\n\r\n");
                BeginNamespace();

                Output.Write(Indent);
                Output.Write("public class ");
                Output.Write(decl.Name);
                Output.Write("XmlRpcClientImplementation : I");
                Output.Write(decl.Name);
                Output.Write("\r\n");
                BeginScope();
                Output.Write(Indent);
                Output.Write("private readonly I");
                Output.Write(decl.Name);
                Output.Write("XmlRpcClientDeclaration Interface;\r\n\r\n");

                Output.Write(Indent);
                Output.Write("public ");
                Output.Write(decl.Name);
                Output.Write("XmlRpcClientImplementation(string url)\r\n");
                BeginScope();
                
                Output.Write(Indent);
                Output.Write("Interface = (I");
                Output.Write(decl.Name);
                Output.Write("XmlRpcClientDeclaration)XmlRpcProxyGen.Create(typeof(I");
                Output.Write(decl.Name);
                Output.Write("XmlRpcClientDeclaration));\r\n");
                Output.Write(Indent);
                Output.Write("Interface.Url = url;\r\n");
                Output.Write(Indent);
                Output.Write("Interface.KeepAlive = true;\r\n");
                EndScope();
                Output.Write("\r\n");

                foreach (RDeclaration member in decl.Items)
                {
                    Trace.Assert(member.DeclarationType == RDeclarationType.MethodDeclaration);
                    CreateXmlRpcClientMethodImplementation(member);
                }
                EndScope();
                EndNamespace();
            }
        }

        protected void CreateXmlRpcClientMethodImplementation(RDeclaration decl)
        {
            int index, count;

            WriteComment(decl);
            Output.Write(Indent);
            Output.Write("public ");
            if (decl.ReturnType == null)
            {
                Output.Write("void ");
            }
            else
            {
                Output.Write(GetCSharpParameterType(decl.ReturnType));
                Output.Write(" ");
            }
            Output.Write(decl.Name);
            if (decl.HasItems)
            {
                Output.Write("(");
                index = 1;
                count = decl.Items.Count;
                foreach (RDeclaration member in decl.Items)
                {
                    Output.Write(GetCSharpParameterType(member));
                    Output.Write(" ");
                    Output.Write(member.Name);
                    if (index++ < count)
                    {
                        Output.Write(", ");
                    }
                }
                Output.Write(")\r\n");
            }
            else
            {
                Output.Write("()\r\n");
            }
            BeginScope();
            Output.Write(Indent);
            bool writeClosingBracket = false;
            if (decl.ReturnType != null)
            {
                switch (decl.ReturnType.DeclarationType)
                {
                    case RDeclarationType.BuiltinTypeReference:
                        Output.Write("return ");
                        break;

                    case RDeclarationType.EnumReference:
                    case RDeclarationType.ClassReference:
                        Output.Write("return XmlDataExchange.");
                        Output.Write(decl.ReturnType.ReferencedName);
                        Output.Write("FromString(");
                        writeClosingBracket = true;
                        break;

                    default:
                        throw new InvalidOperationException(string.Format("Unable to map {0} to a parameter type", decl));
                }
            }
            Output.Write("Interface.");
            Output.Write(decl.Name);
            Output.Write("(");
            index = 1;
            count = decl.Items.Count;
            foreach (RDeclaration member in decl.Items)
            {
                switch (member.DeclarationType)
                {
                    case RDeclarationType.BuiltinTypeReference:
                        Output.Write(member.Name);
                        break;

                    case RDeclarationType.EnumReference:
                    case RDeclarationType.ClassReference:
                        Output.Write("XmlDataExchange.StringFrom");
                        Output.Write(member.ReferencedName);
                        Output.Write("(");
                        Output.Write(member.Name);
                        Output.Write(")");
                        break;

                    default:
                        throw new InvalidOperationException(string.Format("Unable to map {0} to a parameter type", decl));
                }
                if (index++ < count)
                {
                    Output.Write(", ");
                }
            }
            if (writeClosingBracket)
                Output.Write(")");
            Output.Write(");\r\n");
            EndScope();
            Output.Write("\r\n");
        }



        public void CreateXmlRpcServerInterfaceDeclaration(RDeclaration decl)
        {
            string filename = string.Format("I{0}XmlRpcServerDeclaration.cs", decl.Name);
            string pathname = Path.Combine(RootDirectory, Namespace.Name, filename);
            Trace.TraceInformation("Creating {0}", pathname);

            using (Output = File.CreateText(pathname))
            {
                WriteCommonHeader();
                Output.Write("using System;\r\n");
                Output.Write("using CookComputing.XmlRpc;\r\n\r\n");
                BeginNamespace();

                Append(decl, "{0}public class {1}XmlRpcServerDeclaration : MarshalByRefObject\r\n");
                BeginScope();
                Append(decl, "{0}public static I{1} Instance;\r\n\r\n");
                foreach (RDeclaration member in decl.Items)
                {
                    Trace.Assert(member.DeclarationType == RDeclarationType.MethodDeclaration);
                    CreateXmlRpcServerInterfaceMethodDeclaration(member);
                }
                EndScope();
                EndNamespace();
            }
        }

        protected void CreateXmlRpcServerInterfaceMethodDeclaration(RDeclaration decl)
        {
            Newline();
            WriteComment(decl);

            AppendFormat("{0}[XmlRpcMethod(\"{1}.{2}\")]\r\n", Indent, Namespace.Name, decl.Name);


            string returnType = "void";
            if (decl.ReturnType != null)
                returnType = GetXmlRpcParameterType(decl.ReturnType);

            AppendFormat("{0}public {1} {2}(", Indent, returnType, decl.Name);
            
            if (decl.HasItems)
            {
                int index = 1;
                int count = decl.Items.Count;
                foreach (RDeclaration member in decl.Items)
                {
                    AppendFormat("{0} {1}", GetXmlRpcParameterType(member), member.Name);
                    if (index++ < count)
                    {
                        Output.Write(", ");
                    }
                }
                Output.Write(")\r\n");
            }
            else
            {
                Output.Write(")\r\n");
            }
            BeginScope();
            Output.Write(Indent);
            string extraBrackets = "";
            if( decl.ReturnType == null )
            {
                AppendFormat("Instance.{0}(", decl.Name);
            }
            else if( decl.ReturnType.DeclarationType == RDeclarationType.BuiltinTypeReference )
            {
                AppendFormat("return Instance.{0}(", decl.Name);
            }
            else
            {
                AppendFormat("return XmlDataExchange.StringFrom{1}(Instance.{0}(", decl.Name, decl.ReturnType.ReferencedName);
                extraBrackets = ")";
            }
            if( decl.HasItems )
            {
                int index = 1;
                int count = decl.Items.Count;
                foreach (RDeclaration member in decl.Items)
                {
                    if( member.DeclarationType == RDeclarationType.BuiltinTypeReference )
                    {
                        Output.Write(member.Name);
                    }
                    else
                    {
                        Append(member, "XmlDataExchange.{2}FromString({1})");
                    }
                    
                    if (index++ < count)
                    {
                        Output.Write(", ");
                    }
                }
                AppendFormat("{0});\r\n", extraBrackets);

            }
            else
            {
                AppendFormat("{0});\r\n", extraBrackets);
            }
            EndScope();
        }

    }
}
